﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Xaml;
using System.Xaml.Schema;

using agree.semi;

using alib;
using alib.Array;
using alib.Collections;
using alib.Debugging;
using alib.Dictionary;
using alib.String;
using alib.Enumerable;

namespace agree.mrs
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// DO NOT MAKE YOUR INSTANCE 'readonly' (or it won't work)
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("count: {Count}")]
	public struct bag<T> : IReadOnlyList<T> where T : MrsElement
	{
		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public T[] a;

		public void Add(T t)
		{
			int ix = arr.Append(ref a, t);
			if (t.ix == -1)
				t.ix = ix;
		}

		public void Remove(T t)
		{
			arr.RemoveSafe(ref a, t);
		}

		public T this[int ix] { get { return a[ix]; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count { get { return a == null ? 0 : a.Length; } }

		public IEnumerator<T> GetEnumerator() { return a.Enumerator(); }

		IEnumerator IEnumerable.GetEnumerator() { return a.Enumerator(); }

		public void AddRange(params T[] items)
		{
			foreach (var item in items)
				Add(item);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class Mrs : identity_base, ISemantics
	{
		public static Mrs FromTfs(Tfs tfs, Vpm vpm = null)
		{
			if (tfs.SemanticsRestricted)
				throw new Exception("TFS is restricted");

			var ts_mrs = tfs.MrsSlot;
			if (!ts_mrs.IsValid)
				return null;

			return new VpmForward(tfs) { Vpm = vpm ?? tfs.vpm }.build(ts_mrs);
		}

		public Mrs(IIdentity prv)
			: base(prv)
		{
			this._vars = Collection<Variable>.None;
			this._hook = new Hook(this);
			this.RELS.a = Collection<Relation>.None;
			this.HCONS.a = Collection<Qeq>.None;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		Variable[] _vars;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Variable[] Variables { get { return _vars; } }
		public void AddVariable(Variable v) { arr.Append(ref _vars, v); }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly Hook _hook;
		public Hook HOOK { get { return _hook; } }

		public bag<Relation> RELS;

		public bag<Qeq> HCONS;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<MrsElement> Elements
		{
			get
			{
				yield return HOOK;
				foreach (var el in RELS.a)
					yield return el;
				foreach (var el in HCONS.a)
					yield return el;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<ElementRole> ElementRoles
		{
			get { return Elements.SelectMany(el => el.Select(er => new ElementRole(el, er))); }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<_ElementRole> _ElementRoles
		{
			get
			{
				return Elements.SelectMany(el => el._erex().Select(role => new _ElementRole(el, role)));
			}
		}

		public Constant RegisterConstant(String value) { throw not.impl; }
		public Variable CreateInstance(System.Type t)
		{
			throw not.impl;
		}
		public SemType GetSemType<T>() where T : SemType { throw not.impl; }

		public override String ToString()
		{
			var sb = new StringBuilder();

			var var_disp_bits = new BitArray(Variables.Length);

			sb.Append(HOOK.LTOP.FormatFirstUse(var_disp_bits));
			sb.Append(" ");
			sb.AppendLine(HOOK.INDEX.FormatFirstUse(var_disp_bits));

			//sb.Append("XARG ");
			//sb.AppendLine(Xarg.ToString());

			sb.Append("{ ");
			int i;
			for (i = 0; i < RELS.Count; i++)
			{
				if (i > 0)
				{
					sb.AppendLine();
					sb.Append("  ");
				}
				sb.Append(RELS[i].TextFormat(var_disp_bits));
			}
			sb.AppendLine(" }");

			sb.Append("{ ");
			for (i = 0; i < HCONS.Count; i++)
			{
				if (i > 0)
					sb.Append(", ");
				sb.Append(HCONS[i].TextFormat(var_disp_bits));
			}
			sb.Append(" }");

			return sb.ToString();
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DictionaryKeyProperty("Feature"), ContentProperty("Value")]
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class Property : IProperty
	{
		public Property(ISemantics sem)
		{
			this.sem = sem;
		}
		public Property(ISemantics sem, String feature, ISemValue value)
			: this(sem)
		{
			this.name = feature;
			this.value = value;
		}
		public Property(ISemantics sem, String feature, String value)
			: this(sem, feature, new Constant(sem, value))
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ISemantics sem;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		String name;
		public String Name
		{
			get { return this.name; }
			set { this.name = value; }
		}
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		ISemValue value;
		public ISemValue Value
		{
			get { return this.value; }
			set { this.value = value; }
		}

		public override String ToString()
		{
			return String.Format("{0} {1}", name, value);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct Role : IEquatable<Role>, IIdentity, IProperty
	{
		public Role(String name, Variable var)
		{
			this.name = name.ToUpper();
			this.var = var;
		}

		public readonly String name;
		public Variable var;

		public String Name { get { return name; } }

		public Variable Value { get { return var; } }
		ISemValue IProperty.Value { get { return var; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsNull { get { return this.name == null; } }

		public override int GetHashCode()
		{
			return name.GetHashCode() ^ var.GetHashCode();
		}
		public override bool Equals(Object obj)
		{
			return obj is Role && Equals((Role)obj);
		}
		public bool Equals(Role other)
		{
			return name == other.name && var == other.var;
		}
		public override String ToString()
		{
			return String.Format("{0} {1}", name, var);
		}

		IIdentity IIdentity.Trace { get { return null; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct _Role : IEquatable<_Role>, IIdentity, IProperty
	{
		public _Role(String name, ISemValue value)
			: this()
		{
			this.Name = name.ToUpper();
			this.Value = value;
		}
		public _Role(Role role)
			: this()
		{
			this.Name = role.name;
			this.Value = role.var;
		}

		public String Name { get; set; }
		public ISemValue Value { get; set; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsNull { get { return this.Name == null; } }

		public override int GetHashCode()
		{
			return Name.GetHashCode() ^ Value.GetHashCode();
		}
		public override bool Equals(Object obj)
		{
			return obj is _Role && this.Equals((_Role)obj);
		}
		public bool Equals(_Role other)
		{
			return this.Name == other.Name && this.Value == other.Value;
		}
		public override String ToString()
		{
			return String.Format("{0} {1}", Name, Value);
		}

		IIdentity IIdentity.Trace { get { return null; } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct ElementRole : IEquatable<ElementRole>
	{
		public ElementRole(MrsElement element, Role role)
		{
			this.element = element;
			this.role = role;
		}
		public readonly MrsElement element;
		public readonly Role role;
		public override bool Equals(Object obj)
		{
			return obj is ElementRole && Equals((ElementRole)obj);
		}
		public bool Equals(ElementRole other)
		{
			return element == other.element && role.Equals(other.role);
		}
		public override int GetHashCode()
		{
			return element.GetHashCode() ^ role.GetHashCode();
		}
		public override String ToString()
		{
			return String.Format("{0} {1}", element, role);
		}
		public String KeyName
		{
			get
			{
				var ck = element is Hook ? '0' : Char.ToUpper(element.Name[0]);
				return String.Format("{0}{1}-{2}", ck, element.ix, role.name);
			}
		}
	};

	[DebuggerDisplay("{ToString(),nq}")]
	public struct _ElementRole : IEquatable<_ElementRole>
	{
		public _ElementRole(MrsElement element, _Role role)
		{
			this.element = element;
			this.role = role;
		}
		public readonly MrsElement element;
		public readonly _Role role;
		public override bool Equals(Object obj)
		{
			return obj is _ElementRole && Equals((_ElementRole)obj);
		}
		public bool Equals(_ElementRole other)
		{
			return element == other.element && role.Equals(other.role);
		}
		public override int GetHashCode()
		{
			return element.GetHashCode() ^ role.GetHashCode();
		}
		public override String ToString()
		{
			return String.Format("{0} {1}", element, role);
		}
		public String KeyName
		{
			get
			{
				var ck = element is Hook ? '0' : Char.ToUpper(element.Name[0]);
				return String.Format("{0}{1}-{2}", ck, element.ix, role.Name);
			}
		}
	};
}